package club.jdiy.core.convert;

import club.jdiy.core.ex.JDiyConvertException;
import club.jdiy.utils.StringUtils;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JDiy常用数据类型互转器.
 *
 * @author ziquee(子秋) QQ号:39886616 主页: http://www.jdiy.club
 * @version 20201019
 */
public abstract class Converter {
    private static final Map<String, Class<? extends Converter>> providers = new HashMap<>();

    static {
        providers.put(String.class.getName(), StringConverter.class);
        providers.put(Integer.class.getName(), IntegerConverter.class);
        providers.put(Long.class.getName(), LongConverter.class);
        providers.put(Double.class.getName(), DoubleConverter.class);
        providers.put(Date.class.getName(), DateConverter.class);
        providers.put(java.sql.Date.class.getName(),LocalDateConverter.class);
        providers.put(LocalDate.class.getName(), LocalDateConverter.class);
        providers.put(LocalDateTime.class.getName(), LocalDateTimeConverter.class);
        providers.put(Timestamp.class.getName(),LocalDateTimeConverter.class);
        providers.put(Boolean.class.getName(), BooleanConverter.class);
        providers.put(Byte.class.getName(), ByteConverter.class);
        providers.put(Short.class.getName(), ShortConverter.class);
        providers.put(Float.class.getName(), FloatConverter.class);
        providers.put(BigInteger.class.getName(), BigIntegerConverter.class);
        providers.put(BigDecimal.class.getName(), BigDecimalConverter.class);
    }


    /**
     * 根据原对象source创建一个Converter互转器.
     *
     * @param source 源数据对象.
     * @return Converter互转器.通过Converter可以很方便的将源数据对象转换成我们需要的目标数据类型.
     * @throws JDiyConvertException 或源数据类型不能创建转换器，则抛出此运行时异常
     */
    public static Converter of(Object source) {
        if (null == source) return new NullConverter();
        String cn = source.getClass().getName();
        Class<? extends Converter> c = providers.get(cn);

        try {
            if (c != null) return c.getConstructor(source.getClass()).newInstance(source);
        } catch (Exception ignore) {
            ignore.printStackTrace();
        }

        throw new JDiyConvertException("Converter unsupported " + cn + " '" + source.toString() + "'");
    }

    /**
     * 将当前的数据类型自动转换成目标数据类型.
     * 转换规则详见此{@link Converter}类中的各get方法使用说明。
     *
     * @param tClass 要转换成的目标数据类型.
     * @param <T>    转换后的值
     * @return T
     * @throws ReflectiveOperationException
     * @see #getString()
     * @see #getInteger()
     * @see #getLong()
     * @see #getDouble()
     * @see #getFloat()
     * @see #getBoolean()
     * @see #getByte()
     * @see #getShort()
     * @see #getDate()
     * @see #getLocalDate()
     * @see #getLocalDateTime()
     * @see #getBigDecimal()
     * @see #getBigInteger()
     */
    @SuppressWarnings("unchecked")
    public <T> T to(Class<T> tClass) throws ReflectiveOperationException {
        return (T) this.getClass().getMethod("get" +
                ("int".equals(tClass.getSimpleName()) ? "Integer" : StringUtils.capitalize(tClass.getSimpleName()))
        ).invoke(this);
    }


    /**
     * 将源数据类型转换为Byte(byte)类型.
     * <br/><br/>可以转换成Byte的数据类型如下:<br />
     * <ul>
     * <li><strong>boolean</strong> - 当源值为true时，转换为byte值1. 否则转换为byte值0.</li>
     * <li><strong>byte</strong> - 源类型也为byte, 直接返回.</li>
     * <li><strong>double</strong> - 当源double值介于Byte类型的有效范围(Byte.MIN_VALUE~Byte.MAX_VALUE)内时,
     * 则(使用dobleObj.byteValue()方法)完成转换，否则抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>float</strong> - 当源float值介于Byte类型的有效范围(Byte.MIN_VALUE~Byte.MAX_VALUE)内时,
     * 则(使用floatObj.byteValue()方法)完成转换，否则抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>int</strong> - 当源int值介于Byte类型的有效范围(Byte.MIN_VALUE~Byte.MAX_VALUE)内时,
     * 则完成转换，否则抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>long</strong> - 当源long值介于Byte类型的有效范围(Byte.MIN_VALUE~Byte.MAX_VALUE)内时,
     * 则完成转换，否则抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>short</strong> - 当源short值介于Byte类型的有效范围(Byte.MIN_VALUE~Byte.MAX_VALUE)内时,
     * 则完成转换，否则抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>String</strong> - 当源数据类型为空字符串("")时，返回值为null;
     * 否则从字符串的前面往后面截取连续的整数数字部分，
     * 然后使用Byte.parseByte(String)方法完成转换.如果字符串不是以数字开头(或转换时发生错误)，
     * 将抛出{@link JDiyConvertException}异常.</li>
     * (注意此操作可能涉及到四舍入或截断,而导致精度丢失)</li>
     * <li><strong>null</strong> - 当源数据值为null, 返回值也将为null.</li>
     * </ul>
     *
     * @return 转换后的byte值, 或null.
     */
    public Byte getByte() {
        throw __throw(Byte.class);
    }

    /**
     * 将源数据类型转换为boolean类型.
     * <br/><br/>可以转换成boolean的数据类型如下:<br />
     * <ul>
     * <li><strong>boolean</strong> - 当源类型也为boolean，直接返回.</li>
     * <li><strong>byte</strong> - 非0的byte值转换为true, 0值转换为false.</li>
     * <li><strong>double</strong> - 非0的double值转换为true, 0值转换为false.</li>
     * <li><strong>float</strong> - 非0的float值转换为true, 0值转换为false.</li>
     * <li><strong>int</strong> - 非0的int值转换为true, 0值转换为false.</li>
     * <li><strong>long</strong> - 非0的long值转换为true, 0值转换为false.</li>
     * <li><strong>short</strong> - 非0的short值转换为true, 0值转换为false.</li>
     * <li><strong>String</strong> - ""(空字符串), "false","0", "null", "no", "off", "undefined", "否"
     * 这些字符串(不分大小写)转换为false,其它字符串值都将转换为true.</li>
     * <li><strong>BigInteger</strong> - 0值(BigInteger.ZERO)被转换为false, 非0值转换为true.</li>
     * <li><strong>BigDecimal</strong> - 0值(BigDecimal.ZERO)被转换为false, 非0值转换为true.</li>
     * <li><strong>null</strong> - 当源数据值为null, 转换为false.</li>
     * </ul>
     *
     * @return true | false 转换后的布尔值
     */
    public Boolean getBoolean() {
        throw __throw(boolean.class);
    }

    /**
     * 将源数据类型转换为Short(short)类型.
     * <br/><br/>可以转换成Short的数据类型如下:<br />
     * <ul>
     * <li><strong>boolean</strong> - 当源值为true时，转换为short值1. 否则转换为short值0.</li>
     * <li><strong>byte</strong> - 源类型为byte, 直接将其转换为short值.</li>
     * <li><strong>double</strong> - 当源double值介于Short类型的有效范围(Short.MIN_VALUE~Short.MAX_VALUE)内时,
     * 则(使用dobleObj.shortValue()方法)完成转换，否则抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>float</strong> - 当源float值介于Short类型的有效范围(Short.MIN_VALUE~Short.MAX_VALUE)内时,
     * 则(使用floatObj.shortValue()方法)完成转换，否则抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>int</strong> - 当源int值介于Short类型的有效范围(Short.MIN_VALUE~Short.MAX_VALUE)内时,
     * 则完成转换，否则抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>long</strong> - 当源long值介于Short类型的有效范围(Short.MIN_VALUE~Short.MAX_VALUE)内时,
     * 则完成转换，否则抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>short</strong> - 源值为short, 直接返回该值.</li>
     * <li><strong>String</strong> - 当源值为空字符串("")时，返回值为null;
     * 否则从字符串的前面往后面截取连续的整数数字部分，
     * 然后使用Short.parseShort(String)方法完成转换.如果字符串不是以数字开头(或转换时发生错误)，
     * 将抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>BigInteger</strong> - 直接调用BigInteger对象的toShort()方法完成转换.</li>
     * <li><strong>BigDecimal</strong> - 直接调用BigDecimal对象的toShort()方法完成转换.
     * (注意此操作可能涉及到四舍入或截断,而导致精度丢失)</li>
     * <li><strong>null</strong> - 当源值为null, 返回值也将为null.</li>
     * </ul>
     *
     * @return 转换后的short值, 或null.
     */
    public Short getShort() {
        throw __throw(Short.class);
    }

    /**
     * 将源数据类型转换为Integer(int)类型.
     * <br/><br/>可以转换成Integer的数据类型如下:<br />
     * <ul>
     * <li><strong>boolean</strong> - 当源值为true时，转换为int值1. 否则转换为int值0.</li>
     * <li><strong>byte</strong> - 源类型为byte, 直接将其转换为int值.</li>
     * <li><strong>double</strong> - 当源double值介于int类型的有效范围(Integer.MIN_VALUE~Integer.MAX_VALUE)内时,
     * 则(使用dobleObj.intValue()方法)完成转换，否则抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>float</strong> - 以四舍五入的方式，将该float值转化为int整数值.</li>
     * <li><strong>int</strong> - 当源值也为整型时，直接返回该值.</li>
     * <li><strong>long</strong> - 当源long值介于Integer类型的有效范围(Integer.MIN_VALUE~Integer.MAX_VALUE)内时,
     * 则完成转换，否则抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>short</strong> - 源值为short, 直接将其转换为int值.</li>
     * <li><strong>String</strong> - 当源值为空字符串("")时，返回值为null;
     * 否则从字符串的前面往后面截取连续的整数数字部分，
     * 然后使用Integer.parseInt(String)方法完成转换.如果字符串不是以数字开头(或转换时发生错误)，
     * 将抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>BigInteger</strong> - 直接调用BigInteger对象的intValue()方法完成转换.</li>
     * <li><strong>BigDecimal</strong> - 直接调用BigDecimal对象的intValue()方法完成转换.
     * (注意此操作可能涉及到四舍入或截断,而导致精度丢失)</li>
     * <li><strong>null</strong> - 当源值为null, 返回值也将为null.</li>
     * </ul>
     *
     * @return 转换后的int值, 或null.
     */
    public Integer getInteger() {
        throw __throw(Integer.class);
    }

    /**
     * 将源数据类型转换为Long(long)类型.
     * <br/><br/>可以转换成Long的数据类型如下:<br />
     * <ul>
     * <li><strong>boolean</strong> - 当源值为true时，转换为long值1. 否则转换为long值0.</li>
     * <li><strong>byte</strong> - 源类型为byte, 直接将其转换为long值.</li>
     * <li><strong>java.util.Date</strong> - 取其毫秒值返回.</li>
     * <li><strong>java.time.LocalDate</strong> - 取其毫秒值返回.</li>
     * <li><strong>java.time.LocalDateTime</strong> - 取其毫秒值返回.</li>
     * <li><strong>double</strong> - 以四舍五入的方式，将该double值转化为long整数值.</li>
     * <li><strong>float</strong> - 以四舍五入的方式，将该float值转化为long整数值.</li>
     * <li><strong>int</strong> - 当源值为整型时，直接将其转换为long值.</li>
     * <li><strong>long</strong> - 当源值也是long型，直接返回该long值.</li>
     * <li><strong>short</strong> - 源值为short, 直接将其转换为long值.</li>
     * <li><strong>String</strong> - 当源值为空字符串("")时，返回值为null;
     * 否则从字符串的前面往后面截取连续的整数数字部分，
     * 然后使用Long.parseLong(String)方法完成转换.如果字符串不是以数字开头(或转换时发生错误)，
     * 将抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>BigInteger</strong> - 直接调用BigInteger对象的longValue()方法完成转换.</li>
     * <li><strong>BigDecimal</strong> - 直接调用BigDecimal对象的longValue()方法完成转换.
     * (注意此操作可能涉及到四舍入或截断,而导致精度丢失)</li>
     * <li><strong>null</strong> - 当源值为null, 返回值也将为null.</li>
     * </ul>
     *
     * @return 转换后的long值, 或null.
     */
    public Long getLong() {
        throw __throw(Long.class);
    }

    /**
     * 将源数据类型转换为Float(float)类型.
     * <br/><br/>可以转换成Float的数据类型如下:<br />
     * <ul>
     * <li><strong>boolean</strong> - 当源值为true时，转换为float值1. 否则转换为float值0.</li>
     * <li><strong>byte</strong> - 源类型为byte, 直接将其转换为float值.</li>
     * <li><strong>double</strong> - 调用doubleObj.floatValue()将其转化为float值. 此操作可能损失精度.</li>
     * <li><strong>float</strong> - 源值类型也为float时,直接返回该float值.</li>
     * <li><strong>int</strong> - 当源值为整型时，直接将其转换为float值.</li>
     * <li><strong>long</strong> - 调用longObj.floatValue()，将其转换为float值.</li>
     * <li><strong>short</strong> - 源值为short, 直接将其转换为float值.</li>
     * <li><strong>String</strong> - 当源值为空字符串("")时，返回值为null;
     * 否则从字符串的前面往后面截取连续的数字部分，
     * 然后使用Float.parseFloat(String)方法完成转换.如果字符串不是以数字开头(或转换时发生错误)，
     * 将抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>BigInteger</strong> - 直接调用BigInteger对象的floatValue()方法完成转换.</li>
     * <li><strong>BigDecimal</strong> - 直接调用BigDecimal对象的floatValue()方法完成转换.
     * (如果因BigDecimal数值太大而不能使用float来表示，它会适当地转换为
     * Float.NEGATIVE_INFINITY 或 Float.POSITIVE_INFINITY,即它有可能丢失精度)</li>
     * <li><strong>null</strong> - 当源值为null, 返回值也将为null.</li>
     * </ul>
     *
     * @return 转换后的float值, 或null.
     */
    public Float getFloat() {
        throw __throw(Float.class);
    }

    /**
     * 将源数据类型转换为Double(double)类型.
     * <br/><br/>可以转换成Double的数据类型如下:<br />
     * <ul>
     * <li><strong>boolean</strong> - 当源值为true时，转换为double值1. 否则转换为double值0.</li>
     * <li><strong>byte</strong> - 源类型为byte, 直接将其转换为double值.</li>
     * <li><strong>double</strong> - 当源值也为double型，直接返回该double值.</li>
     * <li><strong>float</strong> - 源值类型为float时,直接转换为double值.</li>
     * <li><strong>int</strong> - 当源值为整型时，直接转换为double值.</li>
     * <li><strong>long</strong> - 调用longObj.doubleValue()，将其转换为double值.</li>
     * <li><strong>short</strong> - 源值为short, 直接将其转换为double值.</li>
     * <li><strong>String</strong> - 当源值为空字符串("")时，返回值为null;
     * 否则从字符串的前面往后面截取连续的数字部分，
     * 然后使用Double.parseDouble(String)方法完成转换.如果字符串不是以数字开头(或转换时发生错误)，
     * 将抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>BigInteger</strong> - 直接调用BigInteger对象的doubleValue()方法完成转换.</li>
     * <li><strong>BigDecimal</strong> - 直接调用BigDecimal对象的doubleValue()方法完成转换.
     * (如果因BigDecimal数值太大而不能使用double来表示，它会适当地转换为
     * Double.NEGATIVE_INFINITY 或 Double.POSITIVE_INFINITY,即它有可能丢失精度)</li>
     * <li><strong>null</strong> - 当源值为null, 返回值也将为null.</li>
     * </ul>
     *
     * @return 转换后的double值, 或null.
     */
    public Double getDouble() {
        throw __throw(Double.class);
    }

    /**
     * 将源数据类型转换为String类型.
     * <ul>
     * <li><strong>boolean</strong> - 注意，为与数据库存储类型互相兼容，布尔值true转换为"1". false转换为"0".</li>
     * <li><strong>java.util.Date</strong> - 日期时间类型将以"yyyy-MM-dd HH:mm:ss"的格式转换为字符串.</li>
     * <li><strong>java.time.LocalDateTime</strong> - 将以"yyyy-MM-dd HH:mm:ss"的格式转换为字符串.</li>
     * <li><strong>java.time.LocalDate</strong> - 将以"yyyy-MM-dd"的格式转换为字符串.</li>
     * <li><strong>byte、double、float、int、long、short, BigInteger</strong> - 当源值类型为这些类型时，
     * 将调用该类型对应的对象封装类的toString()方法将其转换为字符串.</li>
     * <li><strong>BigDecimal</strong> - 将调用BigDecimal对象的toPlainString()方法完成转换,即不使用科学计数值显示。
     * <li><strong>String</strong> - 返回该字符串本身.</li>
     * <li><strong>null</strong> - 当源值为null, 返回值也将为null.</li>
     * </ul>
     *
     * @return 转换后的String值, 或null.
     */
    public String getString() {
        Object s = getSource();
        return s == null ? null : s.toString();
    }

    /**
     * 将源数据类型转换为java.util.Date类型.
     * <br/><br/>可以转换成Date的数据类型如下:<br />
     * <ul>
     * <li><strong>java.util.Date</strong> - 源值类型也为Date类型,直接返回该对象.</li>
     * <li><strong>java.time.LocalDateTime</strong> - 源值类型转换为Date.</li>
     * <li><strong>java.time.LocalDate</strong> - 源值类型转换为Date.</li>
     * <li><strong>long</strong> - 源值类型为long, 将视之为毫秒值，将其转化为Date对象.</li>
     * <li><strong>String</strong> - 当源值为空字符串时，返回null, 如果不是空字符串程序尝试用一些常用的日期时间格式将其转化，
     * 如果不可识别，则抛出{@link JDiyConvertException}异常.可以被JDiy识别的常用日期时间格式如下, 其中“月日时分秒”处的数字小于10时，只有一位数也可以（即前面不补0)也可以：<br />
     * yyyy-MM-dd HH:mm:ss<br />
     * yyyy-MM-dd HH:mm<br />
     * yyyy-MM-dd<br />
     * yyyy年MM月dd日<br />
     * yyyy年MM月dd日 HH:mm:ss<br/>
     * yyyy年MM月dd日 HH:mm<br/>
     * MM/dd/yyyy<br/>
     * MM/dd/yyyy HH:mm:ss<br />
     * MM/dd/yyyy HH:mm<br/></li>
     * <li><strong>null</strong> - 当源值为null, 返回值也将为null.</li>
     * </ul>
     *
     * @return 转换后的Date对象, 或null.
     */
    public Date getDate() {
        throw __throw(Date.class);
    }


    /**
     * 将源数据类型转换为java.math.BigDecimal大数字类型.
     * <br/><br/>根据源数据类型的不同，JDiy转换规则如下:<br />
     * <ul>
     * <li><strong>boolean</strong> - true值被转换为BigDecimal.ONE, false值被转换为BigDecimal.ZERO;</li>
     * <li><strong>String</strong> - 尝试使用java.math.BigDecimal(String val)构造方法进行转换,
     * 如无法构造为BigDecimal对象,将抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>null</strong> - 当源值为null, 返回值也将为null.</li>
     * <li><strong>基本数字类型</strong> - 对于任意的数字类型(byte, double, float, int, long, short),
     * 都将使用java.math.BigDecimal.valueOf(..)静态方法完成BigDecimal封包.</li>
     * <li><strong>BigInteger</strong> -  通过 new BigInteger(BigInteger)构造方法完成转换.</li>
     * </ul>
     *
     * @return 转换后的Date对象, 或null.
     */
    public BigDecimal getBigDecimal() {
        throw __throw(BigDecimal.class);
    }


    /**
     * 将源数据类型转换为java.math.BigInteger大数字类型.
     * <br/><br/>根据源数据类型的不同，JDiy转换规则如下:<br />
     * <ul>
     * <li><strong>boolean</strong> - true值被转换为BigInteger.ONE, false值被转换为BigInteger.ZERO;</li>
     * <li><strong>String</strong> - 尝试使用java.math.BigInteger(String val)构造方法进行转换,
     * 如无法构造为BigInteger对象,将抛出{@link JDiyConvertException}异常.</li>
     * <li><strong>null</strong> - 当源值为null, 返回值也将为null.</li>
     * <li><strong>基本数字类型</strong> - 对于非浮点数字类型(byte, int, long, short),
     * 都将使用java.math.BigInteger.valueOf(..)静态方法完成BigInteger封包; 对于float/double,将使用Math.random(float)四舍五入后完成转换.</li>
     * <li><strong>BigDecimal</strong> - 当源值为BigDecimal, 使用其toBigInteger()方法完成转换.</li>
     * </ul>
     *
     * @return 转换后的Date对象, 或null.
     */
    public BigInteger getBigInteger() {
        throw __throw(BigInteger.class);
    }

    /**
     * 将源数据类型转换为java.time.LocalDate类型.
     * <br/><br/>可以转换成LocalDate类型的数据类型如下:<br />
     * <ul>
     * <li><strong>java.util.Date</strong> - 源值为Date类型,使用东八时区将其转换为LocalDate.</li>
     * <li><strong>java.time.LocalDateTime</strong> - 调用其toLocalDate()方法完成转换.</li>
     * <li><strong>long</strong> - 源值类型为long, 将视之为毫秒值，将其转化为LocalDate.</li>
     * <li><strong>String</strong> - 当源值为空字符串时，返回null, 如果不是空字符串程序尝试用一些常用的日期时间格式将其转化，
     * 如果不可识别，则抛出{@link JDiyConvertException}异常.可以被JDiy识别的常用日期时间格式如下, 其中“月日时分秒”处的数字小于10时，只有一位数也可以（即前面不补0)也可以：<br />
     * yyyy-MM-dd HH:mm:ss<br />
     * yyyy-MM-dd HH:mm<br />
     * yyyy-MM-dd<br />
     * yyyy年MM月dd日<br />
     * yyyy年MM月dd日 HH:mm:ss<br/>
     * yyyy年MM月dd日 HH:mm<br/>
     * MM/dd/yyyy<br/>
     * MM/dd/yyyy HH:mm:ss<br />
     * MM/dd/yyyy HH:mm<br/></li>
     * <li><strong>null</strong> - 当源值为null, 返回值也将为null.</li>
     * </ul>
     *
     * @return 转换后的LocalDate对象, 或null.
     */
    public LocalDate getLocalDate() {
        throw __throw(LocalDate.class);
    }

    /**
     * 将源数据类型转换为java.time.LocalDateTime类型.
     * <br/><br/>可以转换成LocalDateTime类型的数据类型如下:<br />
     * <ul>
     * <li><strong>java.util.Date</strong> - 源值为Date类型,使用东八时区将其转换为LocalDateTime.</li>
     * <li><strong>java.time.LocalDate</strong> - 调用其atStartOfDay()方法完成转换（即时间部分补00:00:00）.</li>
     * <li><strong>long</strong> - 源值类型为long, 将视之为毫秒值，将其转化为LocalDateTime.</li>
     * <li><strong>String</strong> - 当源值为空字符串时，返回null, 如果不是空字符串程序尝试用一些常用的日期时间格式将其转化，
     * 如果不可识别，则抛出{@link JDiyConvertException}异常.可以被JDiy识别的常用日期时间格式如下, 其中“月日时分秒”处的数字小于10时，只有一位数也可以（即前面不补0)也可以：<br />
     * yyyy-MM-dd HH:mm:ss<br />
     * yyyy-MM-dd HH:mm<br />
     * yyyy-MM-dd<br />
     * yyyy年MM月dd日<br />
     * yyyy年MM月dd日 HH:mm:ss<br/>
     * yyyy年MM月dd日 HH:mm<br/>
     * MM/dd/yyyy<br/>
     * MM/dd/yyyy HH:mm:ss<br />
     * MM/dd/yyyy HH:mm<br/></li>
     * <li><strong>null</strong> - 当源值为null, 返回值也将为null.</li>
     * </ul>
     *
     * @return 转换后的LocalDateTime对象, 或null.
     */
    public LocalDateTime getLocalDateTime() {
        throw __throw(LocalDateTime.class);
    }

    /**
     * 返回源数据对象。即{@link Converter#of(Object)}传入的原数据对象。
     */
    abstract Object getSource();

    protected JDiyConvertException __throw(Class<?> c) {
        throw new JDiyConvertException(
                getSource() == null
                        ? "null could not convert to " + c.getName()
                        : "can not convert " + getSource().getClass().getName() + " '" + getString() + "' to "
                        + c.getName());
    }
}
